package com.hnedu.util;

import java.net.MalformedURLException;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.jsp.JspException;

import net.sf.navigator.displayer.MenuDisplayer;
import net.sf.navigator.menu.MenuComponent;
import net.sf.navigator.taglib.ExpressionEvaluator;
import net.sf.navigator.taglib.UseMenuDisplayerTag;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.struts.util.RequestUtils;

import javax.servlet.jsp.tagext.TagSupport;


public class DisplayMenuTag extends TagSupport {

  //~ Instance fields ========================================================

  private Log log = LogFactory.getLog(DisplayMenuTag.class);

  /** Holds value of property name. */
  private String name;

  /** Holds value of property target. */
  private String target;

  //~ Constructors ===========================================================

  /** Creates new DisplayMenuTag */
  public DisplayMenuTag() {
  }

  //~ Methods ================================================================

  /** Getter for property name.
   * @return Value of property name.
   */
  public String getName() {
    return name;
  }

  /** Setter for property name.
   * @param name New value of property name.
   */
  public void setName(String name) {
    if (log.isDebugEnabled()) {
      log.debug("setting name to: " + name);
    }

    this.name = name;
  }

  /** Getter for property target.
   * @return Value of property target.
   */
  public String getTarget() {
    return target;
  }

  /** Setter for property target.
   * @param target New value of property target.
   */
  public void setTarget(String target) {
    this.target = target;
  }

  public int doStartTag() throws JspException {
    MenuDisplayer displayer =
        (MenuDisplayer) pageContext.getAttribute(
        UseMenuDisplayerTag.DISPLAYER_KEY);

    if (displayer == null) {
      throw new JspException("Could not retrieve the menu displayer.");
    }

    MenuComponent menu = (MenuComponent) pageContext.findAttribute(this.name);
    if (menu != null) {
      try {
        // use the overridden target
        if (target != null) {
          displayer.setTarget(this.target);
        }

        // set the location value to use
        // the context relative page attribute
        // if specified in the menu
        try {
          setPageLocation(menu);
        }
        catch (MalformedURLException m) {
          log.error("Incorrect action or forward: " + m.getMessage());
          log.debug("setting location to #");
          menu.setLocation("#");
        }

        displayer.display(menu);
        displayer.setTarget(null);
      }
      catch (Exception e) {
        // don't swallow the exception
        e.printStackTrace();
      }
    }
    else {
      throw new JspException("menu.not.found" + " " + this.name);
    }

    return (SKIP_BODY);
  }

  /**
   * Sets the value for the menu location to the
   * appropriate value if location is null.  If location
   * is null, and the page attribute exists, it's value
   * will be set to the the value for page prepended with
   * the context path of the application.
   *
   * If the page is null, and the forward attribute exists,
   * it's value will be looked up in struts-config.xml.
   *
   *                                     FIXME - ssayles - 121102
   * Ideally, this should happen at menu initialization but
   * I was unable to find a reliable way to get the context path
   * outside of a request.  The performance impact is probably
   * negligable, but it would be better to check for this only once.
   *
   * @param menu The menu component to set the location for.
   */
  protected void setPageLocation(MenuComponent menu) throws
      MalformedURLException, JspException {
    HttpServletRequest request =
        (HttpServletRequest) pageContext.getRequest();

    // if the location attribute is null,
    // then set it with a context relative page
    // attribute if it exists
    if (menu.getLocation() == null) {
      if (menu.getPage() != null) {
        // are we sure we'll get an HttpServletRequest?
        menu.setLocation(
            request.getContextPath() + getPage(menu.getPage()));
      }
      else if (menu.getForward() != null) {
        String fwd =
            RequestUtils.computeURL(
            pageContext,
            menu.getForward(),
            null,
            null,
            null,
            null,
            null,
            false);
        menu.setLocation(fwd);
      }
      else if (menu.getAction() != null) {
        // generate Struts Action URL,
        // this will append Context Path (if any),
        // Servlet Mapping (path mapping or extension mapping)
        // Module Prefix (if any) & Session ID (if any)
        String action =
            RequestUtils.computeURL(
            pageContext,
            null,
            null,
            null,
            menu.getAction(),
            null,
            null,
            false);
        menu.setLocation(action);
      }
    }

    String url = menu.getLocation();

    // Use JSTL to replace dymamic values in the URL
    ExpressionEvaluator eval = new ExpressionEvaluator(this, pageContext);

    String s;
    if ( (s = eval.evalString("url", url)) != null) {
      menu.setUrl(s);
    }

    // do all contained menus
    MenuComponent[] subMenus = menu.getMenuComponents();

    if (subMenus.length > 0) {
      for (int i = 0; i < subMenus.length; i++) {
        setPageLocation(subMenus[i]);
      }
    }
  }

  /**
   * Returns the value with page prepended with a "/"
   * if it is not already.
   *
   * @param page The value for the page.
   */
  private String getPage(String page) {
    if (page.startsWith("/")) {
      return page;
    }
    else {
      page = "/" + page;
    }

    return page;
  }

  public void release() {
    super.release();
    this.name = null;
    this.target = null;
  }

}
